#include <iostream>
#include <fstream>
//#include <time.h>
//#include <memory.h>
#include <math.h>
#include <string.h>
#include "ModelMD2.h"
#include "../Texture.h"
#include "../Logger.h"

void _ModelMD2::clear(){
	face.clear();
	frame.clear();
	uv.clear();
}
bool _ModelMD2::load(const char *filename)
{
	std::ifstream fIn;
	unsigned long fSize;
	unsigned char *data=NULL;
	
	std::vector<MD2TexCoord> md2_uv;
	
	MD2Header head;
	long frameLoop,itemLoop;
	MD2FrameInfo frameInfo;
	
	clear();
	
	
	// Open the specified file
	fIn.open(filename,std::ios::binary);
	
	if(fIn==NULL)
	{
		errLog(Str()<<"Could not open the file \""<<filename<<"\"");
		return false;
	}
	
	// Get file size
	fIn.seekg(0,std::ios_base::end);
	fSize=fIn.tellg();
	fIn.seekg(0,std::ios_base::beg);
	
	// Allocate some space
	data=new unsigned char[fSize];
	
	if(data==NULL)
    {
		fIn.close();
		errLog.print(Str()<<"Could not allocate enough memory to read from the model file \""<<filename<<"\"");
		return false;
    }
	
	// Read the file into memory
	fIn.read((char*)data,fSize);
	
	fIn.close();
	
	
	// Get Header data
	memcpy(&head,data,68);
	
	
	// A few checks to ensure this is an MD2 file
	if(head._id!=844121161) // chars 'IDP2' as a dword
	{
		errLog.print(Str()<<"Header contense from file \""<<filename<<" did not start with \"IDP2\"");
		return false;
	}
	
	if(head.version!=8)
	{
		errLog.print(Str()<<"Do not know how to parse the version "<<head.version<<" of a MD2 file from \""<<filename<<"\"");
		return false;
	}
	
	if((unsigned)head.eofOffset!=fSize)
	{
		errLog.print(Str()<<"Error with the eof offset in file \""<<filename<<"\"!");
		return false;
	}
	
	// Grab the info we'll need later
	int nFrames = head.nFrames;
	int nTri = head.nTriangles;
	int nVtx = head.nVertices;
	int nUV = head.nTexCoords;
	
	// Read first texture name
	if(head.nTextures>0)
    {
		char fn[64];
		memcpy(&fn,&data[head.texOffset],64);
		tex = textureDB.make(fn);
    }
	
	// Read face data
	MD2Face tFace;
	for (int i=0;i<head.nTriangles;i++)
	{
		memcpy(&tFace, &data[head.faceOffset]+(i*sizeof(MD2Face)), sizeof(MD2Face));
		face.push_back(tFace);
	}
	
	// Read MD2 UV data
	MD2TexCoord tCoord;
	for (int i=0;i<head.nTexCoords;i++)
	{
		memcpy(&tCoord, &data[head.uvOffset]+(i*sizeof(MD2TexCoord)), sizeof(MD2TexCoord));
		md2_uv.push_back(tCoord);
	}
	
	// Convert into regular UVs
	Mesh_UV tUV;
	for (itemLoop=0;itemLoop<nUV;itemLoop++)
    {
		
		tUV.u=((float)md2_uv[itemLoop].u)/head.texWidth;
		tUV.v=((float)md2_uv[itemLoop].v)/head.texHeight;
		uv.push_back(tUV);
    }
	
	// Load frame vertex info
	MD2Vtx vtx;
	Mesh_Vtx newvtx;
	for (frameLoop=0;frameLoop<nFrames;frameLoop++)
    {
		// Get frame conversion data
		memcpy(&frameInfo,&data[head.frameOffset + (head.frameSize * frameLoop)],sizeof(frameInfo));
		
		// Read MD2 style vertex data
		memcpy(&vtx,&data[head.frameOffset + (head.frameSize * frameLoop) + sizeof(frameInfo)],sizeof(MD2Vtx));
		
		// Convert vertices
		MD2Frame tFrame;
		for (itemLoop=0;itemLoop<nVtx;itemLoop++)
		{
			newvtx.x=(vtx.vtx[0] * frameInfo.scale[0]) + frameInfo.translate[0];
			newvtx.y=(vtx.vtx[1] * frameInfo.scale[0]) + frameInfo.translate[1];   
			newvtx.z=(vtx.vtx[2] * frameInfo.scale[0]) + frameInfo.translate[2];
			tFrame.vtx.push_back(newvtx);
		}
		frame.push_back(tFrame);
    }
	
	// Finished with vtx and filedata
	delete [] data;
	
	// Calc normals for each frane
	for(frameLoop=0;frameLoop!=nFrames;frameLoop++)
    {
		// Calc face normal
		for(itemLoop=0;itemLoop!=nTri;itemLoop++)
		{
			Mesh_Vtx normal;
			CalcNormal(frame[frameLoop].vtx[face[itemLoop].p1],
					   frame[frameLoop].vtx[face[itemLoop].p2],
					   frame[frameLoop].vtx[face[itemLoop].p3],
					   &normal);
			frame[frameLoop].norm.push_back(normal);
		}
    }
	return true;
}
int _ModelMD2::getFrameCount(){return (int)frame.size();}
void _ModelMD2::draw(int &frame,double scale){
	int part;
	//mainLog(Str()<<"Face size: "<<(int)this->frame.size());
	Str tStr;
	
	// Limit frame range
	if(frame>=getFrameCount())
	{frame=0;}
	
	//glBindTexture(GL_TEXTURE_2D,TexID);
	if (tex!=0){tex->use();}
	
	glBegin(GL_TRIANGLES);
	
	for(part=0;part<(int)face.size();part++)
    {
		mainLog.print(Str()<<"["<<part<<"]"
					  <<this->frame[frame].vtx[face[part].p1].x*scale<<","
					  <<this->frame[frame].vtx[face[part].p1].y*scale<<","
					  <<this->frame[frame].vtx[face[part].p1].z*scale);
		glNormal3f(this->frame[frame].norm[part].x*scale,
				   this->frame[frame].norm[part].y*scale,
				   this->frame[frame].norm[part].z*scale);
		glTexCoord2f(uv[face[part].uv1].u,uv[face[part].uv1].v);
		glVertex3f(this->frame[frame].vtx[face[part].p1].x*scale,
				   this->frame[frame].vtx[face[part].p1].y*scale,
				   this->frame[frame].vtx[face[part].p1].z*scale);
		glTexCoord2f(uv[face[part].uv2].u,uv[face[part].uv2].v);
		glVertex3f(this->frame[frame].vtx[face[part].p2].x*scale,
				   this->frame[frame].vtx[face[part].p2].y*scale,
				   this->frame[frame].vtx[face[part].p2].z*scale);
		glTexCoord2f(uv[face[part].uv3].u,uv[face[part].uv3].v);
		glVertex3f(this->frame[frame].vtx[face[part].p3].x*scale,
				   this->frame[frame].vtx[face[part].p3].y*scale,
				   this->frame[frame].vtx[face[part].p3].z*scale);
    }
	
	glEnd();
	
}
void _ModelMD2::CalcNormal(Mesh_Vtx v1,Mesh_Vtx v2,Mesh_Vtx v3,Mesh_Vtx* Result){
	double v1x,v1y,v1z,v2x,v2y,v2z;
	double nx,ny,nz;
	double vLen;
	
	// Calculate vectors
	v1x = v1.x - v2.x;
	v1y = v1.y - v2.y;
	v1z = v1.z - v2.z;
	
	v2x = v2.x - v3.x;
	v2y = v2.y - v3.y;
	v2z = v2.z - v3.z;
	
	// Get cross product of vectors
	
	nx = (v1y * v2z) - (v1z * v2y);
	ny = (v1z * v2x) - (v1x * v2z);
	nz = (v1x * v2y) - (v1y * v2x);
	
	// Normalise final vector
	vLen = sqrt( (nx * nx) + (ny * ny) + (nz * nz) );
	
	Result->x = (float) (nx / vLen);
	Result->y = (float) (ny / vLen);
	Result->z = (float) (nz / vLen);
}
